use strum_macros::{Display};
use derive_new::new;
use super::mir_ref::{InstrRef, BasicBlockRef};
use crate::mir::reg;
use std::fmt;


#[derive(Debug, new)]
pub struct MirInst {
    pub instr_kind: MirInstKind,
    pub instr_id: usize,
    pub vreg_id : Option<reg::Vreg>,
}

impl MirInst {
    pub fn get_vreg_id(&self) -> String {
        let mut res = String::new();
        match &self.vreg_id {
            Some(v) => {
                res = v.vreg_id.to_string();
            }
            None => {}
        }
        res
    }
}

#[derive(Debug)]
pub enum MirInstKind {
    ReturnInstr(ReturnInstr),
    BinInst(BinInstr),
    SwitchInstr(SwitchInstr),
    GotoInstr(GotoInstr),
    // i => _1 , variable to reg
    Load(Load),
    // 
    // Store()
}

#[derive(Debug, new)]
pub struct Load {
    pub src: String
}

#[derive(Debug, Clone, Copy)]
pub enum Operand {
    ConstValue(i32),
    Vreg(InstrRef),
}

#[derive(Debug, Clone, new)]
pub struct ReturnInstr{
    pub ret_val : Option<Operand>
}

#[derive(Debug, Clone, Display)]
pub enum ArithmeticOp {
    #[strum(serialize = "Add")]
    ADD,
    #[strum(serialize = "Sub")]
    MINUS,
    #[strum(serialize = "Mul")]
    MUL,
}

#[derive(Debug, Clone, Display)]
pub enum BooleanOp {
    #[strum(serialize = "Gt")]
    GT,
    #[strum(serialize = "Lt")]
    LT,
    #[strum(serialize = "Ge")]
    GE,
    #[strum(serialize = "Le")]
    LE,
    #[strum(serialize = "Eq")]
    EQ,
}

#[derive(Debug, Clone)]
pub enum BinOp{
    Arith(ArithmeticOp),
    Boolean(BooleanOp),
}

impl fmt::Display for BinOp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BinOp::Arith(op) => write!(f, "{}", op),   // ArithmeticOp 已经有 Display
            BinOp::Boolean(op) => write!(f, "{}", op), // BooleanOp 已经有 Display
        }
    }
}

#[derive(Debug, Clone, new)]
pub struct BinInstr {
    pub op : BinOp,
    pub lhs : Operand,
    pub rhs : Operand,
}

#[derive(Debug, Clone, new)]
pub struct SwitchInstr {
    pub cond: Operand,
    pub tureBB: BasicBlockRef,
    pub otherwiseBB: Option<BasicBlockRef>,
}

#[derive(Debug, Clone, new)] 
pub struct GotoInstr {
    pub dst: BasicBlockRef
}


